D:\a\tools.proto\tools.proto\compiler\src\compiler\protocol.rs
Line | Count | Source |
1 | | // Copyright (c) 2024, BlockProject 3D |
2 | | // |
3 | | // All rights reserved. |
4 | | // |
5 | | // Redistribution and use in source and binary forms, with or without modification, |
6 | | // are permitted provided that the following conditions are met: |
7 | | // |
8 | | // * Redistributions of source code must retain the above copyright notice, |
9 | | // this list of conditions and the following disclaimer. |
10 | | // * Redistributions in binary form must reproduce the above copyright notice, |
11 | | // this list of conditions and the following disclaimer in the documentation |
12 | | // and/or other materials provided with the distribution. |
13 | | // * Neither the name of BlockProject 3D nor the names of its contributors |
14 | | // may be used to endorse or promote products derived from this software |
15 | | // without specific prior written permission. |
16 | | // |
17 | | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
18 | | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
19 | | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
20 | | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
21 | | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
22 | | // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
23 | | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
24 | | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
25 | | // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
26 | | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
27 | | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
28 | | |
29 | | use crate::compiler::error::Error; |
30 | | use crate::compiler::imports::Import; |
31 | | use crate::compiler::message::{FieldType, Message}; |
32 | | use crate::compiler::r#enum::Enum; |
33 | | use crate::compiler::structure::Structure; |
34 | | use crate::compiler::union::Union; |
35 | | use crate::compiler::util::imports::{ImportSolver, ProtocolStore}; |
36 | | use crate::compiler::util::store::{name_index, ObjectStore}; |
37 | | use crate::compiler::util::types::{Name, TypePathMap}; |
38 | | use crate::model::message::MessageFieldValue; |
39 | | use crate::model::protocol::{Description, Endianness}; |
40 | | use crate::model::typedef::Typedef; |
41 | | use bp3d_debug::{info, trace}; |
42 | | use std::borrow::Cow; |
43 | | use std::collections::BTreeMap; |
44 | | use std::rc::Rc; |
45 | | |
46 | | name_index!(Typedef => name); |
47 | | |
48 | | #[derive(Clone, Debug)] |
49 | | pub struct Protocol { |
50 | | pub full_name: String, |
51 | | pub description: Option<Description>, |
52 | | pub endianness: Endianness, |
53 | | pub type_path_map: TypePathMap, |
54 | | pub structs: ObjectStore<Structure>, |
55 | | pub messages: ObjectStore<Message>, |
56 | | pub enums: ObjectStore<Enum>, |
57 | | pub unions: ObjectStore<Union>, |
58 | | pub types: ObjectStore<Typedef>, |
59 | | } |
60 | | |
61 | | impl bp3d_util::index_map::Index for Protocol { |
62 | | type Key = str; |
63 | | |
64 | 171 | fn index(&self) -> &Self::Key { |
65 | 171 | &self.full_name |
66 | 171 | } |
67 | | } |
68 | | |
69 | | impl Protocol { |
70 | 35 | pub fn name(&self) -> &str { |
71 | 35 | if let Some(id0 ) = self.full_name.rfind("::") { Branch (71:16): [True: 0, False: 35]
Branch (71:16): [Folded - Ignored]
|
72 | 0 | &self.full_name[id + 2..] |
73 | | } else { |
74 | 35 | &self.full_name |
75 | | } |
76 | 35 | } |
77 | | |
78 | 38 | pub fn package(&self) -> &str { |
79 | 38 | if let Some(id0 ) = self.full_name.rfind("::") { Branch (79:16): [True: 0, False: 38]
Branch (79:16): [Folded - Ignored]
|
80 | 0 | &self.full_name[..id] |
81 | | } else { |
82 | 38 | "" |
83 | | } |
84 | 38 | } |
85 | | |
86 | 70 | pub fn iter_codecs(&self) -> impl Iterator<Item = &str> { |
87 | 98 | self.messages.iter().flat_map(|v| v.fields.iter().filter_map(56 |v| v.codec.as_deref())56 ) |
88 | 70 | } |
89 | | |
90 | 55 | pub fn from_model<T: ImportSolver>( |
91 | 55 | mut value: crate::model::Protocol, |
92 | 55 | protocols: &ProtocolStore<T>, |
93 | 55 | package: &str, |
94 | 55 | ) -> Result<Self, Error> { |
95 | 55 | let full_name = if package.is_empty() { Branch (95:28): [True: 17, False: 0]
Branch (95:28): [Folded - Ignored]
Branch (95:28): [True: 18, False: 0]
Branch (95:28): [Folded - Ignored]
Branch (95:28): [True: 5, False: 0]
Branch (95:28): [True: 1, False: 0]
Branch (95:28): [True: 1, False: 0]
Branch (95:28): [True: 4, False: 0]
Branch (95:28): [True: 3, False: 0]
Branch (95:28): [True: 5, False: 0]
Branch (95:28): [True: 1, False: 0]
|
96 | 55 | value.name |
97 | | } else { |
98 | 0 | format!("{}::{}", package, value.name) |
99 | | }; |
100 | 55 | let mut proto = Protocol { |
101 | 55 | full_name, |
102 | 55 | description: value.description, |
103 | 55 | endianness: value.endianness.unwrap_or(Endianness::Little), |
104 | 55 | type_path_map: TypePathMap::new(), |
105 | 55 | structs: ObjectStore::new(), |
106 | 55 | messages: ObjectStore::new(), |
107 | 55 | enums: ObjectStore::new(), |
108 | 55 | unions: ObjectStore::new(), |
109 | 55 | types: ObjectStore::new(), |
110 | 55 | }; |
111 | 55 | info!("Running import solver pass..."); |
112 | 55 | if let Some(mut imports10 ) = value.imports { Branch (112:16): [True: 5, False: 12]
Branch (112:16): [Folded - Ignored]
Branch (112:16): [True: 5, False: 13]
Branch (112:16): [Folded - Ignored]
Branch (112:16): [True: 0, False: 5]
Branch (112:16): [True: 0, False: 1]
Branch (112:16): [True: 0, False: 1]
Branch (112:16): [True: 0, False: 4]
Branch (112:16): [True: 0, False: 3]
Branch (112:16): [True: 0, False: 5]
Branch (112:16): [True: 0, False: 1]
|
113 | 10 | let mut solved_imports = Vec::new(); |
114 | 48 | while let Some(v38 ) = imports.pop() { Branch (114:23): [True: 19, False: 5]
Branch (114:23): [Folded - Ignored]
Branch (114:23): [True: 19, False: 5]
Branch (114:23): [Folded - Ignored]
Branch (114:23): [True: 0, False: 0]
Branch (114:23): [True: 0, False: 0]
Branch (114:23): [True: 0, False: 0]
Branch (114:23): [True: 0, False: 0]
Branch (114:23): [True: 0, False: 0]
Branch (114:23): [True: 0, False: 0]
Branch (114:23): [True: 0, False: 0]
|
115 | 38 | let protocol_path = if package.is_empty() { Branch (115:40): [True: 19, False: 0]
Branch (115:40): [Folded - Ignored]
Branch (115:40): [True: 19, False: 0]
Branch (115:40): [Folded - Ignored]
Branch (115:40): [True: 0, False: 0]
Branch (115:40): [True: 0, False: 0]
Branch (115:40): [True: 0, False: 0]
Branch (115:40): [True: 0, False: 0]
Branch (115:40): [True: 0, False: 0]
Branch (115:40): [True: 0, False: 0]
Branch (115:40): [True: 0, False: 0]
|
116 | 38 | Cow::Borrowed(&v.protocol) |
117 | | } else { |
118 | 0 | Cow::Owned(format!("{}::{}", package, v.protocol)) |
119 | | }; |
120 | 38 | trace!({protocol=&**protocol_path} {type=&*v.type_name}, "Solving import"); |
121 | 38 | let r = protocols.get(&protocol_path); |
122 | 38 | let r = match r { |
123 | 38 | Some(r) => r, |
124 | 0 | None => return Err(Error::UndefinedReference(v.protocol)), |
125 | | }; |
126 | 38 | let ty = r |
127 | 38 | .structs |
128 | 38 | .get(&v.type_name) |
129 | 38 | .map(Import::Struct) |
130 | 38 | .or_else(|| r.messages.get(&v.type_name).map(Import::Message)12 ) |
131 | 38 | .or_else(|| r.unions.get(&v.type_name).map(Import::Union)6 ) |
132 | 38 | .or_else(|| r.enums.get(&v.type_name).map(Import::Enum)4 ) |
133 | 38 | .or_else(|| r.types.get(&v.type_name).map(Import::Type)2 ) |
134 | 38 | .ok_or(Error::UnresolvedImport(format!("{}::{}", protocol_path, v.type_name)))?0 ; |
135 | 38 | let type_path = protocols.get_full_type_path(r, &v.type_name).ok_or(Error::SolverError)?0 ; |
136 | 38 | solved_imports.push(ty); |
137 | 160 | let count1 = imports.iter().filter(|vv| vv.type_name == v.type_name).count(); |
138 | 198 | let count2 = solved_imports.iter().filter(|vv| vv.name() == v.type_name).count()38 ; |
139 | 38 | let is_ambiguous = count1 > 0 || count2 > 136 ; Branch (139:36): [True: 1, False: 18]
Branch (139:36): [Folded - Ignored]
Branch (139:36): [True: 1, False: 18]
Branch (139:36): [Folded - Ignored]
Branch (139:36): [True: 0, False: 0]
Branch (139:36): [True: 0, False: 0]
Branch (139:36): [True: 0, False: 0]
Branch (139:36): [True: 0, False: 0]
Branch (139:36): [True: 0, False: 0]
Branch (139:36): [True: 0, False: 0]
Branch (139:36): [True: 0, False: 0]
|
140 | 38 | proto.type_path_map.add(&ty, type_path); |
141 | 38 | if is_ambiguous { Branch (141:20): [True: 2, False: 17]
Branch (141:20): [Folded - Ignored]
Branch (141:20): [True: 2, False: 17]
Branch (141:20): [Folded - Ignored]
Branch (141:20): [True: 0, False: 0]
Branch (141:20): [True: 0, False: 0]
Branch (141:20): [True: 0, False: 0]
Branch (141:20): [True: 0, False: 0]
Branch (141:20): [True: 0, False: 0]
Branch (141:20): [True: 0, False: 0]
Branch (141:20): [True: 0, False: 0]
|
142 | 4 | trace!({protocol=&**protocol_path} {type=&*v.type_name}, "Import is ambiguous, import it as {}::{}", v.protocol, v.type_name); |
143 | 4 | ty.insert(format!("{}::{}", v.protocol, v.type_name), &mut proto); |
144 | 34 | } else { |
145 | 34 | trace!({protocol=&**protocol_path} {type=&*v.type_name}, "Import is not ambiguous"); |
146 | 34 | ty.insert(v.type_name, &mut proto); |
147 | 34 | } |
148 | | } |
149 | 45 | } |
150 | 55 | info!("Adding typedefs..."); |
151 | 55 | if let Some(types3 ) = value.types { Branch (151:16): [True: 1, False: 16]
Branch (151:16): [Folded - Ignored]
Branch (151:16): [True: 2, False: 16]
Branch (151:16): [Folded - Ignored]
Branch (151:16): [True: 0, False: 5]
Branch (151:16): [True: 0, False: 1]
Branch (151:16): [True: 0, False: 1]
Branch (151:16): [True: 0, False: 4]
Branch (151:16): [True: 0, False: 3]
Branch (151:16): [True: 0, False: 5]
Branch (151:16): [True: 0, False: 1]
|
152 | 11 | for v8 in types { |
153 | 8 | trace!({model=?&v}, "Adding typedef to protocol"); |
154 | 8 | proto.types.insert(Rc::new(v)); |
155 | 8 | } |
156 | 52 | } |
157 | 55 | info!("Running type inference pass..."); |
158 | 55 | if let Some(structs45 ) = &mut value.structs { Branch (158:16): [True: 15, False: 2]
Branch (158:16): [Folded - Ignored]
Branch (158:16): [True: 16, False: 2]
Branch (158:16): [Folded - Ignored]
Branch (158:16): [True: 0, False: 5]
Branch (158:16): [True: 1, False: 0]
Branch (158:16): [True: 1, False: 0]
Branch (158:16): [True: 4, False: 0]
Branch (158:16): [True: 3, False: 0]
Branch (158:16): [True: 5, False: 0]
Branch (158:16): [True: 0, False: 1]
|
159 | 119 | for v74 in structs { |
160 | 205 | for field131 in &mut v.fields { |
161 | 0 | if let Some(info) = Branch (161:28): [True: 0, False: 58]
Branch (161:28): [Folded - Ignored]
Branch (161:28): [True: 0, False: 59]
Branch (161:28): [Folded - Ignored]
Branch (161:28): [True: 0, False: 0]
Branch (161:28): [True: 0, False: 1]
Branch (161:28): [True: 0, False: 1]
Branch (161:28): [True: 0, False: 4]
Branch (161:28): [True: 0, False: 2]
Branch (161:28): [True: 0, False: 6]
Branch (161:28): [True: 0, False: 0]
|
162 | 131 | field.item_type.as_ref().and_then(|v| proto.types.get(v)13 ).and_then(|v| v.to_struct()0 ) |
163 | 0 | { |
164 | 0 | trace!({typedef=?info}, "Inferred {} as {}", field.name, info.name); |
165 | 0 | let name = std::mem::replace(field, info.clone()).name; |
166 | 0 | field.name = name; |
167 | 131 | } |
168 | | } |
169 | | } |
170 | 10 | } |
171 | 55 | if let Some(messages25 ) = &mut value.messages { Branch (171:16): [True: 7, False: 10]
Branch (171:16): [Folded - Ignored]
Branch (171:16): [True: 8, False: 10]
Branch (171:16): [Folded - Ignored]
Branch (171:16): [True: 5, False: 0]
Branch (171:16): [True: 0, False: 1]
Branch (171:16): [True: 0, False: 1]
Branch (171:16): [True: 0, False: 4]
Branch (171:16): [True: 0, False: 3]
Branch (171:16): [True: 5, False: 0]
Branch (171:16): [True: 0, False: 1]
|
172 | 64 | for v39 in messages { |
173 | 103 | for field64 in &mut v.fields { |
174 | 8 | if let Some(info) = Branch (174:28): [True: 2, False: 20]
Branch (174:28): [Folded - Ignored]
Branch (174:28): [True: 6, False: 21]
Branch (174:28): [Folded - Ignored]
Branch (174:28): [True: 0, False: 6]
Branch (174:28): [True: 0, False: 0]
Branch (174:28): [True: 0, False: 0]
Branch (174:28): [True: 0, False: 0]
Branch (174:28): [True: 0, False: 0]
Branch (174:28): [True: 0, False: 9]
Branch (174:28): [True: 0, False: 0]
|
175 | 64 | field.item_type.as_ref().and_then(|v| proto.types.get(v)24 ).and_then(|v| v.to_message()8 ) |
176 | 8 | { |
177 | 8 | trace!({typedef=?info}, "Inferred {} as {}", field.name, info.name); |
178 | 8 | let name = std::mem::replace(field, info.clone()).name; |
179 | 8 | field.name = name; |
180 | 56 | } |
181 | | } |
182 | | } |
183 | 30 | } |
184 | 55 | info!("Running compiler pass..."); |
185 | 55 | if let Some(enums7 ) = value.enums { Branch (185:16): [True: 3, False: 14]
Branch (185:16): [Folded - Ignored]
Branch (185:16): [True: 3, False: 15]
Branch (185:16): [Folded - Ignored]
Branch (185:16): [True: 0, False: 5]
Branch (185:16): [True: 0, False: 1]
Branch (185:16): [True: 0, False: 1]
Branch (185:16): [True: 0, False: 4]
Branch (185:16): [True: 0, False: 3]
Branch (185:16): [True: 0, False: 5]
Branch (185:16): [True: 1, False: 0]
|
186 | 7 | trace!(">> Compiling enums..."); |
187 | 13 | for v7 in enums { |
188 | 7 | trace!({model=?&v}, "Compiling enum"); |
189 | 7 | let v6 = Rc::new(Enum::from_model(v)?1 ); |
190 | 6 | proto.enums.insert(v); |
191 | | } |
192 | 48 | } |
193 | 54 | if let Some(structs45 ) = value.structs { Branch (193:16): [True: 15, False: 2]
Branch (193:16): [Folded - Ignored]
Branch (193:16): [True: 16, False: 2]
Branch (193:16): [Folded - Ignored]
Branch (193:16): [True: 0, False: 5]
Branch (193:16): [True: 1, False: 0]
Branch (193:16): [True: 1, False: 0]
Branch (193:16): [True: 4, False: 0]
Branch (193:16): [True: 3, False: 0]
Branch (193:16): [True: 5, False: 0]
Branch (193:16): [True: 0, False: 0]
|
194 | 45 | trace!(">> Compiling structures..."); |
195 | 110 | for v74 in structs { |
196 | 74 | trace!({model=?&v}, "Compiling structure"); |
197 | 74 | let v65 = Rc::new(Structure::from_model(&proto, v)?9 ); |
198 | 65 | proto.structs.insert(v); |
199 | | } |
200 | 9 | } |
201 | 45 | let mut union_messages = BTreeMap::new(); |
202 | 45 | if let Some(mut messages25 ) = value.messages { Branch (202:16): [True: 7, False: 10]
Branch (202:16): [Folded - Ignored]
Branch (202:16): [True: 8, False: 10]
Branch (202:16): [Folded - Ignored]
Branch (202:16): [True: 5, False: 0]
Branch (202:16): [True: 0, False: 0]
Branch (202:16): [True: 0, False: 0]
Branch (202:16): [True: 0, False: 0]
Branch (202:16): [True: 0, False: 0]
Branch (202:16): [True: 5, False: 0]
Branch (202:16): [True: 0, False: 0]
|
203 | 25 | trace!(">> Extracting unions and messages with references to unions..."); |
204 | 25 | let len = messages.len(); |
205 | 39 | for i in 1..len + 125 { |
206 | 39 | let i = len - i; |
207 | 64 | let has_unions = messages[i].fields.iter().any(|v| match &v.value { |
208 | 16 | None => false, |
209 | 48 | Some(v) => matches!39 (v, MessageFieldValue::Union { .. }), |
210 | 64 | }); |
211 | 39 | if has_unions { Branch (211:20): [True: 2, False: 11]
Branch (211:20): [Folded - Ignored]
Branch (211:20): [True: 2, False: 13]
Branch (211:20): [Folded - Ignored]
Branch (211:20): [True: 0, False: 6]
Branch (211:20): [True: 0, False: 0]
Branch (211:20): [True: 0, False: 0]
Branch (211:20): [True: 0, False: 0]
Branch (211:20): [True: 0, False: 0]
Branch (211:20): [True: 5, False: 0]
Branch (211:20): [True: 0, False: 0]
|
212 | 9 | let msg = messages.remove(i); |
213 | 9 | union_messages.insert(i, msg); |
214 | 30 | } |
215 | | } |
216 | 25 | let pos = len; |
217 | 25 | let len = messages.len(); |
218 | 30 | for i in 1..len + 125 { |
219 | 30 | let i = len - i; |
220 | 30 | let has_ref = union_messages.values().any(|union| messages[i].references(&union.name)6 ); |
221 | 30 | if has_ref { Branch (221:20): [True: 1, False: 10]
Branch (221:20): [Folded - Ignored]
Branch (221:20): [True: 1, False: 12]
Branch (221:20): [Folded - Ignored]
Branch (221:20): [True: 0, False: 6]
Branch (221:20): [True: 0, False: 0]
Branch (221:20): [True: 0, False: 0]
Branch (221:20): [True: 0, False: 0]
Branch (221:20): [True: 0, False: 0]
Branch (221:20): [True: 0, False: 0]
Branch (221:20): [True: 0, False: 0]
|
222 | 2 | let msg = messages.remove(i); |
223 | 2 | union_messages.insert(pos + i, msg); |
224 | 28 | } |
225 | | } |
226 | 25 | trace!(">> Compiling messages with no references to unions..."); |
227 | 48 | for v28 in messages { |
228 | 28 | trace!({model=?&v}, "Compiling message"); |
229 | 28 | let v23 = Rc::new(Message::from_model(&proto, v)?5 ); |
230 | 23 | proto.messages.insert(v); |
231 | | } |
232 | 20 | } |
233 | 40 | if let Some(unions10 ) = value.unions { Branch (233:16): [True: 3, False: 14]
Branch (233:16): [Folded - Ignored]
Branch (233:16): [True: 3, False: 15]
Branch (233:16): [Folded - Ignored]
Branch (233:16): [True: 0, False: 0]
Branch (233:16): [True: 0, False: 0]
Branch (233:16): [True: 0, False: 0]
Branch (233:16): [True: 0, False: 0]
Branch (233:16): [True: 0, False: 0]
Branch (233:16): [True: 4, False: 1]
Branch (233:16): [True: 0, False: 0]
|
234 | 10 | trace!(">> Compiling unions..."); |
235 | 20 | for v10 in unions { |
236 | 10 | trace!({model=?&v}, "Compiling union"); |
237 | 10 | let v = Rc::new(Union::from_model(&proto, v)?0 ); |
238 | 10 | proto.unions.insert(v); |
239 | | } |
240 | 30 | } |
241 | 40 | trace!(">> Compiling messages with references to unions..."); |
242 | 46 | for (_, msg11 ) in union_messages { |
243 | 11 | trace!({model=?&msg}, "Compiling message"); |
244 | 11 | let v6 = Rc::new(Message::from_model(&proto, msg)?5 ); |
245 | 6 | proto.messages.insert(v); |
246 | | } |
247 | | |
248 | 35 | info!("Running list sanitizer pass..."); |
249 | 35 | for msg28 in proto.messages.iter() { |
250 | 28 | if msg.is_embedded() { Branch (250:16): [True: 3, False: 10]
Branch (250:16): [Folded - Ignored]
Branch (250:16): [True: 4, False: 11]
Branch (250:16): [Folded - Ignored]
Branch (250:16): [True: 0, False: 0]
Branch (250:16): [True: 0, False: 0]
Branch (250:16): [True: 0, False: 0]
Branch (250:16): [True: 0, False: 0]
Branch (250:16): [True: 0, False: 0]
Branch (250:16): [True: 0, False: 0]
Branch (250:16): [True: 0, False: 0]
|
251 | 14 | for field in &msg.fields7 { |
252 | 14 | let flag = match &field.ty { |
253 | 2 | FieldType::Container(v) => v.nested, |
254 | 12 | _ => true, |
255 | | }; |
256 | 14 | if !flag { Branch (256:24): [True: 0, False: 6]
Branch (256:24): [Folded - Ignored]
Branch (256:24): [True: 0, False: 8]
Branch (256:24): [Folded - Ignored]
Branch (256:24): [True: 0, False: 0]
Branch (256:24): [True: 0, False: 0]
Branch (256:24): [True: 0, False: 0]
Branch (256:24): [True: 0, False: 0]
Branch (256:24): [True: 0, False: 0]
Branch (256:24): [True: 0, False: 0]
Branch (256:24): [True: 0, False: 0]
|
257 | 0 | return Err(Error::MissingNestedList(format!("{}::{}", msg.name, field.name))); |
258 | 14 | } |
259 | | } |
260 | 21 | } |
261 | | } |
262 | 35 | Ok(proto) |
263 | 55 | } |
264 | | } |